import * as THREE from 'three'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js'
import Cookies from 'js-cookie'

/*
  点云车道线实例打散成可编辑的点和线
  annotationObject：标注目标实例
  fillStyle：点的颜色
  scene：three.js 的场景实例
  track：three.js 垃圾回收器
 */
export function extractPointsLineInstance(annotationObject, fillStyle, scene, track) {
  let pointsIndex = annotationObject.threeD.bndbox.x.split(',')
  const positionArray = scene.points.geometry.attributes.position.array
  let prePoint
  const pointsLineInstance = {
    mMarkAnnotationId: annotationObject.mMarkAnnotationId,
    name: annotationObject.name,
    x: annotationObject.threeD.bndbox.x,
    y: annotationObject.threeD.bndbox.y,
    z: annotationObject.threeD.bndbox.z,
    annotationObject: annotationObject,
    needUpdate: false,
    header: null,
    tmpLinePoints: [],
    tmpLines: [],
    annotationType: 'pointsLine',
    fillStyle: fillStyle,
  }
  const attrs = annotationObject.threeD.other_attributes
  for (let i = 1; i <= 8; i++) {
    pointsLineInstance['a' + i] = attrs['a' + i]
    pointsLineInstance['b' + i] = attrs['b' + i]
  }
  let pointIndex = 0
  pointsIndex.forEach((index) => {
    const position = {
      x: positionArray[3 * index],
      y: positionArray[3 * index + 1],
      z: positionArray[3 * index + 2],
    }
    const linePoint = addPointLineKeypoint(scene, track, position, index, fillStyle)
    linePoint.setPointIndex(pointIndex, scene)

    if (prePoint) {
      const line = addLine(prePoint, linePoint, fillStyle, pointsLineInstance.tmpLines, scene)
      line.instance = pointsLineInstance
    } else {
      pointsLineInstance.header = linePoint
    }
    pointsLineInstance.tmpLinePoints.push(linePoint)
    linePoint.instance = pointsLineInstance
    prePoint = linePoint

    pointIndex++
  })

  return pointsLineInstance
}

export function addPointsLineInstance(annotationObject, fillStyle, scene, track, pointSize = null) {
  annotationObject.fillStyle = fillStyle
  const geometry = track(new THREE.BufferGeometry())
  const canvas = document.createElement('canvas')
  canvas.width = 100
  canvas.height = 100
  const context = canvas.getContext('2d')
  let colors = [1, 1, 0]
  if (fillStyle) {
    context.fillStyle = fillStyle
    let colorValue = fillStyle.replace('rgb(', '').replace(')', '').replace(' ', '')
    colorValue = colorValue.split(',')
    colors = [parseInt(colorValue[0]) / 255.0, parseInt(colorValue[1]) / 255.0, parseInt(colorValue[2]) / 255.0]
  } else {
    context.fillStyle = '#7bbfea'
  }
  context.arc(50, 50, 45, 0, 2 * Math.PI)
  context.fill()

  let pointsIndex = annotationObject.threeD.bndbox.x.split(',')
  const position = []
  const positionArray = scene.points.geometry.attributes.position.array
  pointsIndex.forEach((index) => {
    position.push(positionArray[3 * index], positionArray[3 * index + 1], positionArray[3 * index + 2])
    colors.push(colors[0], colors[1], colors[2])
  })

  const texture = track(new THREE.Texture(canvas))
  texture.needsUpdate = true
  if (!pointSize) {
    pointSize = parseFloat(Cookies.get('pointSize'))
  }
  if (!pointSize) pointSize = 2
  const material = track(
    new THREE.PointsMaterial({
      size: 6,
      vertexColors: THREE.VertexColors,
      depthTest: false,
      map: texture,
    })
  )
  material.sizeAttenuation = false
  geometry.setAttribute('position', new THREE.Float32BufferAttribute(position, 3))
  geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3))

  const pointsLine = track(new THREE.Points(geometry, material))
  pointsLine.visible = true
  pointsLine.pointType = 'pointsLine'
  pointsLine.annotationObject = annotationObject
  scene.add(pointsLine)

  const lineMaterial = new THREE.LineBasicMaterial({ color: context.fillStyle })
  const line = new THREE.Line(geometry, lineMaterial)
  scene.add(line)
  pointsLine.line = line

  if (!scene.pointsLines) {
    scene.pointsLines = []
  }
  scene.pointsLines.push(pointsLine)

  const text = document.createElement('div')
  text.className = 'label'
  text.style.color = context.fillStyle
  text.textContent = annotationObject.labelContent
  const label = new CSS2DObject(text)
  label.position.x = position[position.length - 3]
  label.position.y = position[position.length - 2]
  label.position.z = position[position.length - 1]
  label.tag = 'des_label'
  label.visible = scene.showLabel
  scene.add(label)
  if (!scene.lineList) {
    scene.lineList = []
  }
  scene.lineList.push(label)
  annotationObject.label = label
  pointsLine.label = label
}

/*
  scene：three.js 的场景实例
  track：three.js 垃圾回收器
  position：要添加的点在空间所处位置
  pointsCloudIndex：点在pcd文件中点序号的索引
  fillStyle：点的颜色
 */
export function addPointLineKeypoint(scene, track, position, pointsCloudIndex, fillStyle = null) {
  const geometry = track(new THREE.BufferGeometry())
  const canvas = document.createElement('canvas')
  canvas.width = 100
  canvas.height = 100
  const context = canvas.getContext('2d')
  let colors = [1, 1, 0]
  if (fillStyle) {
    context.fillStyle = fillStyle
    fillStyle = fillStyle.replace('rgb(', '').replace(')', '').replace(' ', '')
    fillStyle = fillStyle.split(',')
    colors = [parseInt(fillStyle[0]) / 255.0, parseInt(fillStyle[1]) / 255.0, parseInt(fillStyle[2]) / 255.0]
  } else {
    context.fillStyle = '#7bbfea'
  }
  context.arc(50, 50, 45, 0, 2 * Math.PI)
  context.fill()
  const texture = track(new THREE.Texture(canvas))
  texture.needsUpdate = true
  const material = track(
    new THREE.PointsMaterial({ size: 8, vertexColors: THREE.VertexColors, depthTest: false, map: texture })
  )
  material.sizeAttenuation = false
  geometry.setAttribute('position', new THREE.Float32BufferAttribute([0, 0, 0], 3))
  geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3))

  const keyPoint = track(new THREE.Points(geometry, material))
  keyPoint.visible = true
  keyPoint.position.copy(position)
  keyPoint.pointType = 'keyPoint'
  keyPoint.pointsCloudIndex = pointsCloudIndex
  scene.add(keyPoint)

  keyPoint.setPointIndex = function (pointIndex, scene) {
    this.pointIndex = pointIndex
    if (this.label) {
      scene.remove(this.label)
    }
    const text = document.createElement('div')
    text.className = 'label'
    text.style.color = 'rgb(150, 150, 150)'
    text.textContent = pointIndex
    text.style.fontSize = '16px'
    this.label = track(new CSS2DObject(text))
    this.label.position.x = this.position.x + 0.1
    this.label.position.y = this.position.y
    this.label.position.z = this.position.z
    this.label.tag = 'des_label'
    scene.add(this.label)
  }

  return keyPoint
}

export function addLine(point1, point2, lineColor, lineArray, scene) {
  const geometry = new THREE.BufferGeometry()
  const positions = []
  positions.push(point1.position.x)
  positions.push(point1.position.y)
  positions.push(point1.position.z)
  positions.push(point2.position.x)
  positions.push(point2.position.y)
  positions.push(point2.position.z)
  geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3))
  // const material = new THREE.LineBasicMaterial({ color: 0x7bbfea })
  const material = new THREE.LineBasicMaterial({ color: lineColor })
  const line = new THREE.Line(geometry, material)

  lineArray.push(line)
  line.lineIndex = lineArray.length - 1
  point1.next = point2
  point1.forwardLine = line
  point2.previous = point1
  point2.backLine = line
  line.point1 = point1
  line.point2 = point2

  scene.add(line)

  return line
}

export function removeLine(line, lineArray, scene) {
  let index = -1
  for (let i = 0; i < lineArray.length; i++) {
    if (line.uuid === lineArray[i].uuid) {
      index = i
      break
    }
  }
  if (index !== -1) {
    lineArray.splice(index, 1)
    scene.remove(line)
  }
}

export function removePoint(delPoint, _self) {
  if (delPoint.label) {
    _self.scene.remove(delPoint.label)
  }
  _self.scene.remove(delPoint)
  _self.tmpLinePoints.splice(delPoint.pointIndex, 1)
  if (delPoint.backLine) {
    removeLine(delPoint.backLine, _self.tmpLines, _self.scene)
    removeLine(delPoint.previous.forwardLine, _self.tmpLines, _self.scene)
  }
  if (delPoint.forwardLine) {
    removeLine(delPoint.forwardLine, _self.tmpLines, _self.scene)
    removeLine(delPoint.next.backLine, _self.tmpLines, _self.scene)
  }
  let point1 = delPoint.previous
  let point2 = delPoint.next
  if (point1) {
    if (!point2) {
      point1.next = null
      point1.forwardLine = null
    }
  }
  if (point2) {
    if (!point1) {
      point2.previous = null
      point2.backLine = null
    }
    let p = point2
    while (p) {
      p.setPointIndex(p.pointIndex - 1, _self.scene)
      p = p.next
    }
  }
  if (point1 && point2) {
    addLine(point1, point2, 0xff00ff, _self.tmpLines, _self.scene)
  }
}
